use core::cell::UnsafeCell;
use core::ptr;
use mlibs::cmsis_rtos2::event_flags::EventFlags;
use mlibs::cmsis_rtos2::message_queue::{mq_size, MessageQueue};
use mlibs::cmsis_rtos2::utils::AlignStore;
use mlibs::cmsis_rtos2::utils::ByteView;
use mlibs::cmsis_rtos2::{OS_OK, OS_WAIT_FOREVER};
use mlibs::periph::uart::UART1;
use mlibs::periph::{config_dma, config_io, interrupt, Interrupt, NVIC};
use mlibs::periph::{dma::DMA1, gpio::PA, rcc::RCC};
use serde::Serialize;

use crate::error::AppErr;
use crate::proto::{make_res, ByteStream};

#[config_io(TX_PA9_AF_PP, RX_PA10_IN_FLOAT)]
struct UartPin;

#[config_dma(DMA1_CH4_SIZE8)]
struct DmaTx;

#[config_dma(DMA1_CH5_SIZE8)]
struct DmaRx;

const MQ_COUNT: usize = 5;
const MQ_SIZE: usize = mq_size::<ByteView>(MQ_COUNT);

unsafe impl Send for CmdUart {}
unsafe impl Sync for CmdUart {}

pub struct CmdUart {
    recv_mq: MessageQueue<ByteView, MQ_COUNT, MQ_SIZE>,
    write_buf: AlignStore<255>,
    recv_buf: AlignStore<255>,
    event_flags: EventFlags,
    recv_index: UnsafeCell<usize>,
}

pub static CMD_UART: CmdUart = CmdUart {
    recv_mq: MessageQueue::const_new(),
    write_buf: AlignStore::const_new(),
    recv_buf: AlignStore::const_new(),
    event_flags: EventFlags::const_new(),
    recv_index: UnsafeCell::new(0),
};

const FLAG_DMA_TC: u32 = 0x01;

impl CmdUart {
    pub fn init(&self) {
        UartPin.init();
        DmaRx.init_rcc();
        DmaTx.init_rcc();

        RCC.apb2enr.modify(|_, w| w.usart1en().enabled());

        UART1.cr1.reset();
        UART1.cr2.reset();
        UART1.cr3.write(|w| w.dmar().enabled().dmat().enabled());
        UART1.brr.write(|w| unsafe { w.bits(72000000 / 115200) });
        UART1.cr1.write(|w| {
            w.ue()
                .enabled()
                .idleie()
                .enabled()
                .te()
                .enabled()
                .re()
                .enabled()
        });

        unsafe {
            self.recv_mq.create("cmd_uart");
            self.event_flags.create("cmd_uart");
            self.event_flags.set_flags(FLAG_DMA_TC);

            DmaRx.read_with_circ(
                UART1.dr.as_ptr() as u32,
                self.recv_buf.as_ptr() as u32,
                self.recv_buf.size() as u16,
            );
            NVIC::unmask(Interrupt::DMA1_CHANNEL4);
            NVIC::unmask(Interrupt::USART1);
        }
    }

    pub fn recv_with_idle(&self) -> ByteView {
        unsafe { self.recv_mq.take() }
    }

    pub fn write(&self, buf: &[u8]) {
        unsafe {
            self.event_flags
                .wait_any_flags(FLAG_DMA_TC, OS_WAIT_FOREVER);
            ptr::copy_nonoverlapping(buf.as_ptr(), self.write_buf.as_mut_ptr(), buf.len());
            DmaTx.write_with_it(
                UART1.dr.as_ptr() as u32,
                self.write_buf.as_ptr() as u32,
                buf.len() as u16,
            );
        }
    }

    pub fn send<T: Serialize>(&self, ctype: u8, seq: u8, ec_or_cmd: u8, value: &T) -> Result<(), AppErr> {
        unsafe {
            self.event_flags.wait_any_flags(FLAG_DMA_TC, OS_WAIT_FOREVER);
            let buf = self.write_buf.as_mut_slice();
            let ret = make_res(buf, ctype, seq, ec_or_cmd, value);
            let len = match ret {
                Err(e) => {
                    self.event_flags.set_flags(FLAG_DMA_TC);
                    return Err(e);
                },
                Ok(len) => {
                    len
                }
            };
            
            DmaTx.write_with_it(
                UART1.dr.as_ptr() as u32, 
                self.write_buf.as_ptr() as u32, 
                len as u16
            );
        }

        Ok(())
    }

    unsafe fn recv_idle(&self) {
        let index = self.recv_buf.size() - DmaRx.ndtr() as usize;
        let recv_index = self.recv_index.get().read_volatile();
        if index == recv_index {
            return;
        }
        if index > recv_index {
            self.recv_mq.post(self.recv_buf.bw_slice(recv_index, index));
        } else {
            self.recv_mq.post(self.recv_buf.bw_slice_from(recv_index));
            if index != 0 {
                self.recv_mq.post(self.recv_buf.bw_slice_to(index));
            }
        }
        self.recv_index.get().write_volatile(index);
    }
}

impl ByteStream for &CmdUart {
    #[inline(always)]
    fn recv(&self) -> ByteView {
        self.recv_with_idle()
    }

    fn recv_with_timeout(&self, timeout: u32) -> Option<ByteView> {
        unsafe {
            let mut v = ByteView::null();
            if self.recv_mq.poll(&mut v, timeout) == OS_OK {
                Some(v)
            } else {
                None
            }
        }
    }
}

#[interrupt]
unsafe fn DMA1_CHANNEL4() {
    if DmaTx.is_complete() {
        DmaTx.clear_flags();
        CMD_UART.event_flags.set_flags(FLAG_DMA_TC);
    }
}

#[interrupt]
unsafe fn USART1() {
    if UART1.sr.read().idle().bit_is_set() {
        UART1.dr.read();
        CMD_UART.recv_idle();
    }
}
